package cn.sdstudio.service.impl;


import cn.sdstudio.constants.RedisKey;
import cn.sdstudio.config.CustomUserDetails;
import cn.sdstudio.entity.User;
import cn.sdstudio.entity.UserRole;
import cn.sdstudio.dto.LoginDTO;
import cn.sdstudio.dto.RegistrationDTO;
import cn.sdstudio.dto.UserUpdateDTO;
import cn.sdstudio.vo.GraphicVO;
import cn.sdstudio.vo.LoginVO;
import cn.sdstudio.vo.UserInfoVO;
import cn.sdstudio.mapper.UserMapper;
import cn.sdstudio.mapper.UserRoleMapper;
import cn.sdstudio.service.UserService;
import cn.sdstudio.utils.JwtUtils;
import cn.sdstudio.utils.SecurityUtils;
import com.wf.captcha.SpecCaptcha;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author Zyq
 * @version 1.0
 * @date 2025/5/16 周五
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {

    private final AuthenticationManager authenticationManager;

    private final PasswordEncoder bCryptPasswordEncoder;

    private final StringRedisTemplate redisTemplate;

    private final UserMapper userMapper;

    private final UserRoleMapper userRoleMapper;


    /**
     * 用户登录操作
     *
     * @param loginDTO 登录实体
     * @return 返回令牌
     */
    @Override
    public LoginVO login(LoginDTO loginDTO) {
        log.info("尝试登录: {}", loginDTO);

        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        String code = loginDTO.getCode();
        String codeKey = loginDTO.getCodeKey();

        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            throw new RuntimeException("用户名或密码为空");
        }

        // 验证码逻辑
        if (StringUtils.isBlank(code) || StringUtils.isBlank(codeKey)) {
            throw new RuntimeException("验证码或验证码Key为空");
        }


        String codeInRedis = redisTemplate.opsForValue().get(codeKey);
        if (codeInRedis == null) {
            throw new RuntimeException("验证码已过期");
        }

        if (!code.trim().equalsIgnoreCase(codeInRedis.trim())) {
            throw new RuntimeException("验证码错误");
        }

        // 验证码通过后删除，避免重复使用
        redisTemplate.delete(codeKey);

        try {
            Authentication authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            Authentication authentication = authenticationManager.authenticate(authenticationToken);
            SecurityContextHolder.getContext().setAuthentication(authentication);

            Object principal = authentication.getPrincipal();
            Long userId;
            String usernameForToken;

            if (principal instanceof CustomUserDetails userDetails) {
                userId = userDetails.getId();
                usernameForToken = userDetails.getUsername();
                log.info("认证成功: 用户ID={}, 用户名={}", userId, usernameForToken);
            } else if (principal instanceof org.springframework.security.core.userdetails.User springUser) {
                usernameForToken = springUser.getUsername();
                log.warn("使用默认SpringSecurity User，无法获取数据库ID，用户名：{}", usernameForToken);
                throw new IllegalStateException("无法获取用户ID，UserDetails 不是 CustomUserDetails 类型");
            } else if (principal instanceof String strUser) {
                log.warn("Principal 是 String 类型: {}", strUser);
                throw new IllegalStateException("无法获取用户ID，Principal 是 String 类型");
            } else {
                log.error("未知Principal类型: {}", principal.getClass().getName());
                throw new IllegalStateException("认证成功，但无法识别用户类型");
            }

            log.info("用户权限: {}", authentication.getAuthorities());

            return new LoginVO(JwtUtils.createToken(userId, usernameForToken));
        } catch (UsernameNotFoundException | BadCredentialsException e) {
            throw new RuntimeException("用户名或密码错误");
        } catch (DisabledException e) {
            throw new RuntimeException("账户已被禁用");
        } catch (LockedException e) {
            throw new RuntimeException("账户已被锁定");
        } catch (IllegalStateException e) {
            throw new RuntimeException(e.getMessage());
        } catch (Exception e) {
            log.error("认证异常", e);
            throw new RuntimeException("认证失败，请稍后再试");
        }
    }

    /**
     * 注册默认为 普通用户
     *
     * @param registrationDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer register(RegistrationDTO registrationDTO) {

        if (registrationDTO == null) {
            throw new RuntimeException("参数为空");
        }


        if (userMapper.selectByUsername(registrationDTO.getUsername()) != null) {
            throw new RuntimeException("用户名已经存在");
        }

        registrationDTO.setPassword(bCryptPasswordEncoder.encode(registrationDTO.getPassword()));


        String defaultIcon = "https://q.qlogo.cn/g?b=qq&nk=1650387921&s=100";
        String defaultDes = "这个用户很懒,什么也没有说";
        User user = User.builder()
                .icon(defaultIcon)
                .description(defaultDes)
                .level(1)
                .gender(3)
                .punchCount(0)
                .distanceKm(new BigDecimal(0))
                .shareCount(0)
                .status(1)
                .build();

        BeanUtils.copyProperties(registrationDTO, user);
        userMapper.insert(user);

        //注册到数据库
        return userRoleMapper.insert(UserRole.builder().userId(user.getId()).roleId(2L).build());
    }

    /**
     * 生成图形验证码
     *
     * @return base64
     */
    @Override
    public GraphicVO getGraphicCode() {
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
        String key = RedisKey.USER_LOGIN + UUID.randomUUID();
        String value = specCaptcha.text().toLowerCase();

        log.info(key + ":" + value);

        redisTemplate.opsForValue().set(key, value, 60, TimeUnit.SECONDS);
        return new GraphicVO(specCaptcha.toBase64(), key);
    }

    /**
     * 更新用户信息
     *
     * @param updateDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateByUser(UserUpdateDTO updateDTO) {

        CustomUserDetails userDetails
                = SecurityUtils.getCurrentUser().orElseThrow(() -> new RuntimeException("未登录用户"));

        log.info("当前用户信息:{}", userDetails);

        User userByDb = userMapper.selectById(userDetails.getId());
        log.info("数据库中的用户信息:{}", userByDb.toString());

        BeanUtils.copyProperties(updateDTO, userByDb);
        userByDb.setIcon(userByDb.getIcon());
        return userMapper.updateById(userByDb);
    }

    /**
     * 获取用户信息
     *
     * @return
     */
    @Override
    public UserInfoVO getInfo() {

        CustomUserDetails userDetails
                = SecurityUtils.getCurrentUser().orElseThrow(() -> new RuntimeException("未登录用户"));

        log.info("getinfo当前用户信息:{}", userDetails);

        UserInfoVO info = new UserInfoVO();
        BeanUtils.copyProperties(userMapper.selectById(userDetails.getId()), info);

        return info;
    }


}
